package ConnectWordsTool;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.nio.file.NotDirectoryException;
import java.util.HashMap;
import java.util.Scanner;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import project.LearningTool;



/**
 * A simple learning tool that allows the user to connect
 * two and two words together. This may be for learning
 * languages, concepts, rhymes etc.
 * 
 * ------------------------------------------------------------------
 * The code in this learning tool shows use of directory listing,
 * file reading and parsing of a new constructed file format.
 * ------------------------------------------------------------------
 *  
 * The exercises (or tests) for this learning tool is saved
 * in the directory ConnectWords and have the extension .cwe (ConnectWords exercise)
 *     Their syntax is:
 * DESCRIPTION A one-line text that describes the test.    
 * 
 * first word
 * it's answer
 * 
 * # this is a comment.
 * second word
 * second word's answer
 * 
 * third word
 * third word's answer
 * 
 * PAGEBREAK
 * 
 * new word
 * the answer
 * 
 * 
 * @author Knut
 */
public class ConnectWordsTool extends LearningTool {

	/**
	 * The path to the directory with the exercise-files. 
	 */
	public static final String DIRECTORY = "ConnectWords";
	
	/**
	 * Names on built in tests
	 * 
	 * These tests are embedded in the JAR-file, and is in addition to they existing in the DIRECTORY.
	 * This is because we want to pack the whole runnable project in one jar-file.
	 */
	public static final String BUILTIN_EXERCISES = "/ConnectWordsTool/Matematikk.cwe|/ConnectWordsTool/Norsk - Engelsk.cwe|/ConnectWordsTool/Veldig liten test.cwe";
	
	/**
	 * Extension of a exercise-file.
	 */
	public final String EXTENSION = ".cwe";
	
	/**
	 * Holds the list of the available exercises
	 */
	HashMap<String, URL> exercises;
	
	/**
	 * States of this tool.
	 */
	public enum State { SELECT_EXERCISE, RUN_EXERCISE, DISPLAY_SCORE };
	
	/**
	 * Current state
	 */
	private State state = null;
	
	private SelectExercisePanel selectPanel = new SelectExercisePanel(this);	
	private RunExercisePanel runPanel;
	private DisplayScorePanel scorePanel;
	
	/**
	 * The active inner panel
	 */
	private JPanel innerPanel = null;

	
	
	public ConnectWordsTool() {
		super();
		mToolPanel.setPreferredSize(new Dimension(800, 600));
		mToolPanel.setLayout(new BorderLayout());
		setState(State.SELECT_EXERCISE);
	}
	
	
	public void setState(State state) {
		if (this.state == state) {
			return;
		}
		
		if (innerPanel != null) {
			mToolPanel.remove(innerPanel);
			innerPanel = null;
		}
		
		if (state == State.SELECT_EXERCISE) {
			innerPanel = selectPanel;
			mToolPanel.add(innerPanel);
			mToolPanel.validate();
			mToolPanel.repaint();
		}
		
		if (state == State.RUN_EXERCISE) {
			innerPanel = runPanel;
			mToolPanel.add(innerPanel);
			mToolPanel.validate();
			mToolPanel.repaint();
		}
		
		if (state == State.DISPLAY_SCORE) {
			innerPanel = scorePanel;
			mToolPanel.add(innerPanel);
			mToolPanel.validate();
			mToolPanel.repaint();
		}
	}
	
	
	/**
	 * Run an exercise by file path
	 * @param path
	 */
	public void runExercise(URL path) {
		try {
			runPanel = new RunExercisePanel(this, parseFile(path));
			setState(State.RUN_EXERCISE);
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(mToolPanel, "Could not open exercise.", "Error", JOptionPane.ERROR_MESSAGE);
		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(mToolPanel, "Exercise did not contain any tests.", "Error", JOptionPane.ERROR_MESSAGE);
		}		
	}
	
	/**
	 * Run an exercise
	 * @param exercise
	 */
	public void runExercise(ConnectWordsExercise exercise) {
		try {
			runPanel = new RunExercisePanel(this, exercise);
			setState(State.RUN_EXERCISE);
		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(mToolPanel, "Exercise did not contain any tests.", "Error", JOptionPane.ERROR_MESSAGE);
		}		
	}
	
	
	/**
	 * Display the score of the exercise.
	 * @param exercise
	 */	
	public void displayScore(ConnectWordsExercise exercise) {
		runPanel = null;
		scorePanel = new DisplayScorePanel(this, exercise);
		setState(State.DISPLAY_SCORE);
	}
	
	
	/**
	 * Reads a directory with exercises.
	 * @param directory
	 * @return HashMap of Names and file paths. (the names are parsed from the filename)
	 * @throws NotDirectoryException 
	 */
	public HashMap<String, URL> getExercises(String directory) throws Exception {
		HashMap<String, URL> buffer = new HashMap<String, URL>();
		
		// Tests in DIRECTORY:
		File dir = new File(directory);
		if (dir.isDirectory()) {
			File[] files = dir.listFiles();
			for (int i = 0; i < files.length; i++) {
				File file = files[i];
				
				if (file.getName().substring(file.getName().length()-4).equals(EXTENSION)) {
					buffer.put(file.getName().substring(0, file.getName().length()-4), file.toURI().toURL());
				}
			}
		}
		
		
		// Builtin tests:
		String[] tests = BUILTIN_EXERCISES.split("\\|");
		for (int i = 0; i < tests.length; i++) {
			String filename = tests[i];
			
			if (filename.trim().length() < 1) {
				continue;
			}
			
			if (filename.lastIndexOf("/") > -1) {
				filename = filename.substring(filename.lastIndexOf("/")+1);
			}
			buffer.put(filename.substring(0, filename.length()-4), getClass().getResource(filename));
		}
		
		return buffer;
	}

	/**
	 * Reads a file that contains a exercise.
	 * @param input
	 * @throws FileNotFoundException 
	 */
	public ConnectWordsExercise parseFile(URL path) throws FileNotFoundException {		
		if (path == null) {
			return null;
		}
		
		ConnectWordsExercise exercise = new ConnectWordsExercise();
		ConnectWordsPage page = new ConnectWordsPage();
		
		String line;
		String description = "";
		Scanner in;
		try {
			in = new Scanner(path.openStream());
		} catch (IOException e) {
			return null;
		}
		String leftWord = "";
		String rightWord = "";
		while(in.hasNextLine()) {
			line = in.nextLine();
			
			
			// Skip comments:
			if (line.trim().length() >= 1 && line.trim().substring(0, 1).equals("#")) {
				continue;
			}
			
			// Empty lines:
			if (line.trim().length() == 0) {
				if (leftWord.length() > 0 && rightWord.length() > 0) {
					page.addWords(leftWord, rightWord);
				}
				
				leftWord = "";
				rightWord = "";
				continue;
			}
			
			
			// Description
			if (line.trim().length() >= 11 && line.trim().substring(0, 11).equals("DESCRIPTION")) {
				description = line.substring(11).trim();
				page.setDescription(description);
				continue;
			}
			
			// Pagebreaks:
			if (line.trim().length() >= 9 && line.trim().substring(0, 9).equals("PAGEBREAK")) {
				exercise.addPage(page);
				page = new ConnectWordsPage();
				page.setDescription(description);
				continue;
			}
			
			// Words:
			if (leftWord.length() > 0) {
				rightWord = line.trim();
			} else {
				leftWord = line.trim();
			}	
		}
		
		if (leftWord.length() > 0 && rightWord.length() > 0) {
			page.addWords(leftWord, rightWord);
		}
		
		
		exercise.addPage(page);
		return exercise;
	}

}